﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Interpreter
{
	internal class InstructionBuilder
	{
		private static readonly Dictionary<char, int> opCodeToIntCode = new Dictionary<char, int>()
		{
			{ 'a', Interpreter.BC_ACTIVE_EXCEPTION },
			{ 'b', Interpreter.BC_ADD },
			{ 'c', Interpreter.BC_APPLY_ARG_N },
			{ 'd', Interpreter.BC_ARG_CHECK },
			{ 'e', Interpreter.BC_ASSIGN },
			{ 'f', Interpreter.BC_BASE_CLASS_CONS },
			{ 'g', Interpreter.BC_BITWISE_AND },
			{ 'h', Interpreter.BC_BITWISE_OR },
			{ 'i', Interpreter.BC_BITWISE_XOR },
			{ 'j', Interpreter.BC_BOOLEAN_NOT },
			{ 'k', Interpreter.BC_BRACKET },
			{ 'l', Interpreter.BC_BRACKET_WRAPPED },
			{ 'm', Interpreter.BC_CLASS },
			{ 'n', Interpreter.BC_CLASS_INIT_COMPLETE },
			{ 'o', Interpreter.BC_CONSTRUCTOR_CALL },
			{ 'p', Interpreter.BC_CREATE_LIST },
			{ 'q', Interpreter.BC_CREATE_MAP },
			{ 'r', Interpreter.BC_DIVIDE },
			{ 's', Interpreter.BC_DOT },
			{ 't', Interpreter.BC_DOT_WRAPPED },
			{ 'u', Interpreter.BC_EQUALS },
			{ 'v', Interpreter.BC_FALSE },
			{ 'w', Interpreter.BC_FLOAT },
			{ 'x', Interpreter.BC_FUNCTION_CALL },
			{ 'y', Interpreter.BC_GREATER_THAN },
			{ 'z', Interpreter.BC_GREATER_THAN_OR_EQUAL },
			{ 'A', Interpreter.BC_INCREMENT_PREFIX },
			{ 'B', Interpreter.BC_INCREMENT_POSTFIX },
			{ 'C', Interpreter.BC_INSTANCE_FIELD_ASSIGN },
			{ 'D', Interpreter.BC_INTEGER },
			{ 'E', Interpreter.BC_JUMP },
			{ 'F', Interpreter.BC_JUMP_IF_FALSE },
			{ 'G', Interpreter.BC_JUMP_IF_FALSE_NO_POP },
			{ 'H', Interpreter.BC_JUMP_IF_NO_ARG_PRESENT },
			{ 'I', Interpreter.BC_JUMP_IF_TRUE },
			{ 'J', Interpreter.BC_JUMP_IF_TRUE_NO_POP },
			{ 'K', Interpreter.BC_KEEP_AND_DEREF_POINTER },
			{ 'L', Interpreter.BC_LESS_THAN },
			{ 'M', Interpreter.BC_LESS_THAN_OR_EQUAL },
			{ 'N', Interpreter.BC_MOD },
			{ 'O', Interpreter.BC_MULTIPLY },
			{ 'P', Interpreter.BC_NATIVE_FUNCTION },
			{ 'Q', Interpreter.BC_NEGATIVE },
			{ 'R', Interpreter.BC_NOOP },
			{ 'S', Interpreter.BC_NOT_EQUAL },
			{ 'T', Interpreter.BC_NULL },
			{ 'U', Interpreter.BC_POP },
			{ 'V', Interpreter.BC_RETURN },
			{ 'W', Interpreter.BC_STACK_DATA },
			{ 'X', Interpreter.BC_STRING },
			{ 'Y', Interpreter.BC_SUBTRACT },
			{ 'Z', Interpreter.BC_SWITCH },
			{ '0', Interpreter.BC_THIS },
			{ '1', Interpreter.BC_THROW },
			{ '2', Interpreter.BC_THROW_BUBBLING },
			{ '3', Interpreter.BC_TRUE },
			{ '4', Interpreter.BC_TRY_POP },
			{ '5', Interpreter.BC_TRY_PUSH }, 
			{ '6', Interpreter.BC_VARIABLE },
			{ '7', Interpreter.BC_VARIABLE_WRAPPED },
			{ '8', Interpreter.BC_XOR },

			{ '9', Interpreter.BC_INVOKE_STATIC_CONSTRUCTOR },
			{ '@', Interpreter.BC_ARG_PRE_VERIFY },
		};

		public int[] OpCodes { get; private set; }
		public int[] FirstParam { get; private set; }
		public int[][] MultiParam { get; private set; }

		public InstructionBuilder(string rawInstructions)
		{
			int[][] ops = PerformFirstPass(rawInstructions);

			int[] opCodes = new int[ops.Length];
			int[] firstParam = new int[ops.Length];
			int[][] multiParam = new int[ops.Length][];
			
			PerformSecondPass(ops, opCodes, firstParam, multiParam);

			this.OpCodes = opCodes;
			this.FirstParam = firstParam;
			this.MultiParam = multiParam;
		}

		private void PerformSecondPass(
			int[][] data,
			int[] opCodes,
			int[] firstParam,
			int[][] multiParam)
		{
			int[] cell;
			List<int> multiValue = new List<int>();
			for (int i = 0; i < data.Length; ++i)
			{
				cell = data[i];
				opCodes[i] = cell[0];
				if (cell.Length >= 2)
				{
					firstParam[i] = cell[1];
				}
				if (cell.Length > 2)
				{
					multiValue.Clear();
					for (int j = 2; j < cell.Length; ++j)
					{
						multiValue.Add(cell[j]);
					}
					multiParam[i] = multiValue.ToArray();
				}
			}
		}

		private int[][] PerformFirstPass(string rawInstructions)
		{
			List<int[]> cells = new List<int[]>();
			int index = 0;
			int length = rawInstructions.Length;
			int opCode;
			char c;
			List<int> instruction = new List<int>();
			while (index < length)
			{
				c = rawInstructions[index];
				opCode = opCodeToIntCode[c];

				instruction.Add(opCode);

				switch (opCode)
				{
					case Interpreter.BC_APPLY_ARG_N:
					case Interpreter.BC_ARG_CHECK:
					case Interpreter.BC_BASE_CLASS_CONS:
					case Interpreter.BC_CLASS:
					case Interpreter.BC_CLASS_INIT_COMPLETE:
					case Interpreter.BC_CONSTRUCTOR_CALL:
					case Interpreter.BC_CREATE_LIST:
					case Interpreter.BC_CREATE_MAP:
					case Interpreter.BC_DOT:
					case Interpreter.BC_DOT_WRAPPED:
					case Interpreter.BC_FLOAT:
					case Interpreter.BC_FUNCTION_CALL:
					case Interpreter.BC_INCREMENT_POSTFIX:
					case Interpreter.BC_INCREMENT_PREFIX:
					case Interpreter.BC_INTEGER:
					case Interpreter.BC_INVOKE_STATIC_CONSTRUCTOR:
					case Interpreter.BC_JUMP:
					case Interpreter.BC_JUMP_IF_FALSE:
					case Interpreter.BC_JUMP_IF_FALSE_NO_POP:
					case Interpreter.BC_JUMP_IF_NO_ARG_PRESENT:
					case Interpreter.BC_JUMP_IF_TRUE:
					case Interpreter.BC_JUMP_IF_TRUE_NO_POP:
					case Interpreter.BC_NATIVE_FUNCTION:
					case Interpreter.BC_STACK_DATA:
					case Interpreter.BC_STRING:
					case Interpreter.BC_TRY_PUSH:
					case Interpreter.BC_VARIABLE:
					case Interpreter.BC_VARIABLE_WRAPPED:
						index = this.ReadCommaHexNumbers(instruction, rawInstructions, index + 1);
						break;

					case Interpreter.BC_ACTIVE_EXCEPTION:
					case Interpreter.BC_ARG_PRE_VERIFY:
					case Interpreter.BC_ADD:
					case Interpreter.BC_ASSIGN:
					case Interpreter.BC_BOOLEAN_NOT:
					case Interpreter.BC_BRACKET:
					case Interpreter.BC_BRACKET_WRAPPED:
					case Interpreter.BC_DIVIDE:
					case Interpreter.BC_EQUALS:
					case Interpreter.BC_FALSE:
					case Interpreter.BC_GREATER_THAN:
					case Interpreter.BC_GREATER_THAN_OR_EQUAL:
					case Interpreter.BC_KEEP_AND_DEREF_POINTER:
					case Interpreter.BC_LESS_THAN:
					case Interpreter.BC_LESS_THAN_OR_EQUAL:
					case Interpreter.BC_MOD:
					case Interpreter.BC_MULTIPLY:
					case Interpreter.BC_NEGATIVE:
					case Interpreter.BC_NOT_EQUAL:
					case Interpreter.BC_NULL:
					case Interpreter.BC_POP:
					case Interpreter.BC_RETURN:
					case Interpreter.BC_SUBTRACT:
					case Interpreter.BC_THIS:
					case Interpreter.BC_THROW:
					case Interpreter.BC_TRUE:
					case Interpreter.BC_TRY_POP:
						break;

					default:
						throw new Exception("Unknown op code: " + opCode);
				}

				cells.Add(instruction.ToArray());
				instruction.Clear();

				++index;
			}

			return cells.ToArray();
		}

		private int ReadCommaHexNumbers(List<int> instruction, string rawInstructions, int index)
		{
			char c;
			int num;
			string currentNumber = "";
			do
			{
				c = rawInstructions[index];
				if (c == ',' || c == '|')
				{
					num = Util.ConvertFromHex(currentNumber);
					instruction.Add(num);
					currentNumber = "";
				}
				else
				{
					currentNumber += c;
				}
				++index;
			} while (c != '|');
			return index - 1;
		}
	}
}
